﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

using Moe.ECamel.Common.Contracts;
using Moe.ECamel.Common.Entities;
using Moe.ECamel.Common.Security;
using Moe.ECamel.Server.BL;
using Moe.ECamel.Server.Demo;

namespace Moe.ECamel.Server.Services
{
	/// <summary>
	/// Encapsulates system service interface.
	/// </summary>
	public class SystemService : ECamelService, ISystemService
	{
		#region ISystemService Members

		public ECamelSystemInfo GetSystemInfo()
		{
			using (var bl = new SystemBL(GetReadOnlyDataContext()))
			{
				var result = bl.GetSystemInfo();

				if (result != null)
					ECamelServer.Initialize();
				return result;
			}
		}

		public LoginResponse Login(LoginRequest request)
		{
			using (var userService = new UserService(this))
			{
				// Validate username and password:
				var user = userService.GetUser(request.Username, request.HashedPassword);
				if (user == null)
					return new LoginResponse {LoginError = new ECamelError(MessageIds.SecurityInvalidCredentials)};

				// Initialize response:
				var loginResponse = new LoginResponse();
				loginResponse.UserId = user.UserId;
				loginResponse.Username = user.Username;
				// Get user operations:
				IList<Operation> operations = userService.GetUserOperations(user.UserId);
				loginResponse.Operations = new HashSet<String>();
				foreach (var operation in operations)
					loginResponse.Operations.Add(operation.OperationName);
				// Get primary role:
				loginResponse.PrimaryRole = user.GetPrimaryRole();
				// Get user's customer and library info, if needed:
				if (loginResponse.PrimaryRole != Roles.Admin)
				{
					using (var customerService = new CustomerService(this))
					{
						loginResponse.Customer = customerService.GetCustomerByUserId(user.UserId);
						if (loginResponse.Customer != null)
						{
							loginResponse.Library = loginResponse.Customer.Library;
							if (!loginResponse.Library.Active)
								loginResponse.LoginError = new ECamelError(MessageIds.LoginInvalidInactiveLibrary, loginResponse.Library.LibraryName);
						}
					}
				}

				return loginResponse;
			}
		}

		public void ResetDatabase(ECamelSetupInfo setupInfo)
		{
			DeleteDatabase();
			CreateDatabase(setupInfo.DatabasePath);

			ECamelDataContext dataContext = GetReadWriteDataContext();
			// Generate initial system data:
			using (var bl = new SystemBL(dataContext))
			{
				bl.GenerateSystemBasicInfo();
			}
			// intermediate submit (some are required later):
			SubmitChanges();

			// Create admin user:
			User adminUser = new User { FirstName = "מנהל", LastName = "מערכת",
				Username = setupInfo.AdminUsername, HashedPassword = setupInfo.AdminPassword };
			adminUser.SetDefaultValues();
			foreach (Role role in dataContext.Roles)
				adminUser.UserRoles.Add(new UserRoles { User = adminUser, Role = role });

			using (var userService = new UserService(this))
			{
				userService.CreateUser(adminUser);
			}
			SubmitChanges();

			// Generate demo data if needed:
			if (setupInfo.GenerateDemoData)
			{
				// Save current principal:
				var originalSecurityPrincipal = Thread.CurrentPrincipal;
				// Login as administrator:
				var loginInfo = Login(new LoginRequest {Username = adminUser.Username, HashedPassword = adminUser.HashedPassword});
				Thread.CurrentPrincipal = new ECamelPrincipal(loginInfo);
				// Generate demo data:
				DemoDataGenerator.GenerateAll();
				// Restore original security principal:
				Thread.CurrentPrincipal = originalSecurityPrincipal;
			}
		}
		
		public void DeleteDatabase()
		{
			ECamelDataContext dataContext = CreateECamelDataContext(null);
			try
			{
				if (dataContext.DatabaseExists())
					dataContext.DeleteDatabase();
			}
			finally
			{
				dataContext.Close();
			}
		}

		private void CreateDatabase(String databasePath)
		{
			if (!Directory.Exists(databasePath))
				Directory.CreateDirectory(databasePath);

			ECamelDataContext dataContext = CreateECamelDataContext(databasePath);
			try
			{
				dataContext.CreateDatabase();
			}
			finally
			{
				dataContext.Close();
			}
		}

		public void DeleteUserData()
		{
			using (var bl = new SystemBL(GetReadWriteDataContext()))
			{
				bl.DeleteUserData();
			}
			this.SubmitChanges();
		}

		#endregion
	}
}